/**
 * 
 */
package net.sppan.blog.utils;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.event.CacheEventListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LocalCache<K, V> implements Map<K, V> {

	private static Logger logger = LoggerFactory.getLogger(LocalCache.class);

	/**
	 * ehcached控制器
	 */
	private CacheManager manager = CacheManager.create();

	private static Thread evictThread;

	private final Ehcache cache;

	private class MyEntry implements Entry<K, V> {

		private K k;
		private V v;

		/**
		 * @param k
		 * @param v
		 */
		public MyEntry(K k, V v) {
			super();
			this.k = k;
			this.v = v;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.Map.Entry#getKey()
		 */
		@Override
		public K getKey() {
			return k;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.Map.Entry#getValue()
		 */
		@Override
		public V getValue() {
			return v;
		}

		@Override
		public V setValue(V value) {
			this.v = value;
			return this.v;
		}

	}

	/**
	 * @param cache
	 */
	public LocalCache(String name, int maxELement) {
		super();
		cache = new Cache(name, maxELement, false, false, 0, 0);
		manager.addCache(cache);
		runEvictThead();
	}

	public static Map<String, Integer> getAllCacheSize() {
		CacheManager m = CacheManager.create();
		String[] names = m.getCacheNames();
		HashMap<String, Integer> rs = new HashMap<String, Integer>();
		for (String n : names) {
			rs.put(n, m.getCache(n).getSize());
		}
		return rs;
	}

	public static synchronized void runEvictThead() {
		if (evictThread == null) {
			evictThread = new Thread() {

				@Override
				public void run() {
					while (!Thread.currentThread().isInterrupted()) {
						try {
							CacheManager m = CacheManager.create();
							String[] names = m.getCacheNames();
							for (String n : names) {
								int befor = m.getCache(n).getSize();
								m.getCache(n).evictExpiredElements();
								int after = m.getCache(n).getSize();
								logger.error(n + "-" + befor + "/" + after);
							}
						} catch (Exception e) {
							logger.error("clean element. ", e);
						}

						try {
							TimeUnit.MINUTES.sleep(5);
						} catch (InterruptedException e) {
							Thread.currentThread().interrupt();
						}
					}
				}

			};
			evictThread.setName("EvictThread");
			evictThread.setDaemon(true);
			evictThread.start();
		}
	}

	/**
	 * 注册事件监听器
	 * 
	 * @param listener
	 * @return
	 */
	public boolean registerListener(CacheEventListener listener) {
		return cache.getCacheEventNotificationService().registerListener(listener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#size()
	 */
	@Override
	public int size() {
		return cache.getKeys().size();
		// return cache.getSize();
	}

	public void evictExpiredElements() {
		cache.evictExpiredElements();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#isEmpty()
	 */
	@Override
	public boolean isEmpty() {
		return cache.getSize() == 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#containsKey(java.lang.Object)
	 */
	@Override
	public boolean containsKey(Object key) {
		return cache.get(key) != null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#containsValue(java.lang.Object)
	 */
	@Override
	public boolean containsValue(Object value) {
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#get(java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public V get(Object key) {
		Element ele = cache.get(key);
		if (ele != null)
			return (V) ele.getObjectValue();
		else
			return null;
	}

	@SuppressWarnings("unchecked")
	public V get(Object key, V def) {
		Element ele = cache.get(key);
		if (ele != null)
			return (V) ele.getObjectValue();
		else
			return def;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#put(java.lang.Object, java.lang.Object)
	 */
	@Override
	public V put(K key, V value) {
		Element ele = new Element(key, value);
		cache.put(ele);
		return value;
	}

	public V put(K key, V value, int timeToLiveSeconds) {
		Element ele = new Element(key, value);
		ele.setTimeToLive(timeToLiveSeconds);
		cache.put(ele);
		return value;
	}

	/**
	 * 设置空闲时间或者存活时间
	 * 
	 * @param key
	 * @param value
	 * @param isLive
	 * @param seconds
	 * @return
	 */
	public V put(K key, V value, boolean live, int seconds) {
		Element ele = new Element(key, value);
		if (live)
			ele.setTimeToLive(seconds);
		else
			ele.setTimeToIdle(seconds);
		cache.put(ele);
		return value;
	}

	public void setTimeToLive(K key, int seconds) {
		Element ele = cache.get(key);
		ele.setTimeToLive(seconds);
	}

	public void setTimeToIdle(K key, int seconds) {
		Element ele = cache.get(key);
		ele.setTimeToIdle(seconds);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#remove(java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public V remove(Object key) {
		Element ele = cache.get(key);
		if (ele == null)
			return null;
		cache.removeElement(ele);
		return (V) ele.getObjectValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#putAll(java.util.Map)
	 */
	@Override
	public void putAll(Map<? extends K, ? extends V> m) {
		for (K k : m.keySet()) {
			put(k, m.get(k));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#clear()
	 */
	@Override
	public void clear() {
		cache.removeAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#keySet()
	 */
	@Override
	public Set<K> keySet() {
		@SuppressWarnings("unchecked")
		List<K> lst = cache.getKeys();
		HashSet<K> set = new HashSet<K>(lst);
		return set;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#values()
	 */
	@Override
	public Collection<V> values() {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Map#entrySet()
	 */
	@Override
	public Set<java.util.Map.Entry<K, V>> entrySet() {
		Set<K> set = keySet();
		HashSet<java.util.Map.Entry<K, V>> entris = new HashSet<Map.Entry<K, V>>();
		for (K k : set) {
			V v = get(k);
			if (v != null)
				entris.add(new MyEntry(k, v));
		}
		return entris;
	}

	public V putIfAbsent(K key, V value, boolean live, int seconds) {
		Element ele = new Element(key, value);
		if (live)
			ele.setTimeToLive(seconds);
		else
			ele.setTimeToIdle(seconds);
		Element rs = cache.putIfAbsent(ele);
		if (rs != null) {
			return (V) rs.getObjectValue();
		} else
			return null;
	}

	public V putIfAbsent(K key, V value) {
		Element ele = new Element(key, value);
		Element rs = cache.putIfAbsent(ele);
		if (rs != null)
			return (V) rs.getObjectValue();
		else
			return null;
	}

}
